﻿using System;
using System.IO;
using System.Linq;
using System.Management;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Xsl;
using AdaptationProject.Properties;
using SharedTypes;
using AdaptationProject.Utils;
using System.Windows.Forms;
using System.Collections.Generic;
using GeneticAlgorithm;


namespace AdaptationProject.Utils
{
    /// <summary>
    /// Класс, отвечающий за генерирование отчётов о 
    /// проведённом процессе адаптации.
    /// </summary>
    public static class ReportGenerator
    {
        /// <summary>
        /// Генерирует отчёт о выполненой адаптации.
        /// </summary>
        /// <param name="info">Объект, содержащий отчётные данные.</param>
        /// <param name="info">Объект, содержащий данные о каждом файле.</param>
        /// <param name="info">Метод адаптация.</param>
        /// <param name="info">Имя исполнительного файла.</param>
        public static void GenerateReport(ReportInformationContainer info,
                                          IList<ReportInformationContainer> infoList1,
                                          IAdoptationMethod adoMethod,
                                          string exeName)
        {
            XDocument document = ParseInfo(info, infoList1, adoMethod, exeName);
            try
            {
                string curDateTime = DateTime.Now.ToString().Replace(':', '.').Replace('/', '.');
                StreamWriter outfile =
                    new StreamWriter(string.Format(@"{0}\{1}.html", MySettings.ReportsStoragePath,
                        info.AlgorithmName + "_" + info.MethodName + "_" + curDateTime));

                using (XmlWriter writer = XmlWriter.Create(outfile))
                {
                    // Load the style sheet.
                    XslCompiledTransform xslt = new XslCompiledTransform();
                    xslt.Load(MySettings.CurrentDirectoryPath + "\\XSL\\XSLTMergeFiles.xsl");
                    // Execute the transform and output the results to a writer.
                    xslt.Transform(document.CreateReader(), writer);
                }
            }
            catch (NotSupportedException e)
            {
                MessageBox.Show("Ошибка при создании отчета" + e.Message, "Ошибка отчета", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            catch (Exception e)
            {
                MessageBox.Show("Ошибка при создании отчета" + e.Message, "Ошибка отчета", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// Создаёт XML документ в памяти для отображения отчётных данных.
        /// </summary>
        /// <param name="info">Объект, содержащий отчётные данные.</param>
        /// <returns>Сформированный XML документ.</returns>
        private static XDocument ParseInfo(ReportInformationContainer info,
                                           IList<ReportInformationContainer> infoList1,
                                           IAdoptationMethod adoMethod,
                                           string exeName)
        {
            List<object> geneticParams;
            adoMethod.GetParameters(out geneticParams);
                XDocument doc = new XDocument(
                        new XDeclaration("1.0", "utf-8", "yes"),
                        new XElement("Result",
                        new XElement("Date", info.LaunchDate.ToString()),
                        new XElement("AlgName", info.AlgorithmName),
                        new XElement("MethodName", info.MethodName),
                        new XElement("ExeFile", exeName),
                        new XElement("ExperimentCount", info.ExperimentCount),
                        new XElement("SearchingTime", info.SearchingTimeFomatted),
                        new XElement("Files"),
                        new XElement("Parameters"),
                        new XElement("AllSize", info.CalculateDirSize()),
                        new XElement("МахGeneration", geneticParams[GeneticAlgorithmSettings.MaxGenOrder]),
                        new XElement("NumberIndividualsInThePopulation", geneticParams[GeneticAlgorithmSettings.PopulationOrder]),
                        new XElement("CrossProbability", 0.5d),
                        new XElement("MutationProbability", 0.5d),
                        new XElement("Efficiency"))
                  );

            XElement settings = new XElement("Config",
                                                new XElement("Processor", GetProcessorID()),
                                                new XElement("Hdd", GetHddInfo()),
                                                new XElement("Memory", GetMemoryAmount()));



            double regTime = 0, regCompression = 0;
            for (int i = 0; i < infoList1.Count; ++i)
            {
                try
                {
                    XElement xElement2 = new XElement("File",
                    new XElement("Name", infoList1[i].FileNames[0].Name),
                    new XElement("Real_size", infoList1[i].CalculateDirSize()),

                    new XElement("ElapsedTimeReccommend", infoList1[i].RecommendedResultTimeFomatted),
                    new XElement("ElapsedTime", infoList1[i].TotalElapsedTimeFomatted),
                    new XElement("Recommended_size", infoList1[i].recommendedResult),
                    new XElement("Best_size", Math.Round(infoList1[i].bestSize)),
                    new XElement("Sup_time", (infoList1[i].CalculateSupTime() * 100).ToString() + "%"),
                    new XElement("Sup_compression", (infoList1[i].CalculateSupCompression() * 100).ToString() + "%"));

                    doc.Descendants("Files").First().Add(xElement2);
                    regTime += infoList1[i].CalculateRegTime() * 100;
                    regCompression += infoList1[i].CalculateRegCompression() * 100;
                }
                catch (FileNotFoundException exp)   
                {

                }
            }

            XElement xElement = new XElement("Efficiency",
                new XElement("Sup_time1", (info.CalculateSupTime() * 100).ToString() + "%"),
                new XElement("Reg_time1", (Math.Round(regTime / infoList1.Count,4)).ToString() + "%"),
                new XElement("Sup_compression1", (info.CalculateSupCompression() * 100).ToString() + "%"),
                new XElement("Reg_compression1", (Math.Round(regCompression / infoList1.Count, 4)).ToString() + "%"));

            doc.Descendants("Result").First().Add(xElement);

            foreach (KeyValuePair<int, SharedTypes.LinkedParameter> parameter in info.FindedParameters)
            {
                XElement xElementParameter = new XElement("Parameter",
                    new XElement("Name", parameter.Value.parameter.Name),
                    new XElement("ShortDescription", parameter.Value.parameter.ShortDescription),
                    new XElement("Default", parameter.Value.parameter.DefaultValue),
                    new XElement("Value", parameter.Value.parameter.CurrentValue));
                doc.Descendants("Parameters").First().Add(xElementParameter);
            }
            doc.Descendants("Result").First().Add(settings);

            string paramsStr = "";
            foreach (KeyValuePair<int, SharedTypes.LinkedParameter> parameter in info.FindedParameters)
            {
                int val = parameter.Value.parentKey;
                while (val > 0)
                {
                    paramsStr += " ";
                    --val;
                }
                paramsStr += "•" + parameter.Value.parameter.Name + ": " 
                           + parameter.Value.parameter.CurrentValue + "\n";
                
            }
            XElement xElementParameter22 = new XElement("Item1", paramsStr);
            doc.Descendants("Result").First().Add(xElementParameter22);

            doc.Descendants("Result").First().Add(settings);
            return doc;
        }

        /// <summary>
        /// Получает информацию о процессоре рабочей станции.
        /// </summary>
        /// <returns>Информация о процессоре рабочей станции в строковом представлении.</returns>
        private static string GetProcessorID()
        {
            string sCpuInfo = String.Empty;

            //*** Declare Management Class
            ManagementClass clsMgtClass = new ManagementClass("Win32_Processor");
            ManagementObjectCollection colMgtObjCol = clsMgtClass.GetInstances();

            //*** Loop Over Objects
            foreach (ManagementObject objMgtObj in colMgtObjCol)
            {
                //*** Only return cpuInfo from first CPU
                if (sCpuInfo == String.Empty)
                {
                    sCpuInfo = objMgtObj.Properties["Manufacturer"].Value.ToString();
                    sCpuInfo += " " + objMgtObj.Properties["NumberOfCores"].Value + "Cores";
                    sCpuInfo += " " + objMgtObj.Properties["CurrentClockSpeed"].Value + "MHz";
                }
            }

            return sCpuInfo;
        }

        /// <summary>
        /// Получает информацию о количестве оперативной памяти на рабочей станции.
        /// </summary>
        /// <returns>Информация о количестве оперативной памяти на рабочей станции 
        /// в строковом представлении.</returns>
        private static string GetMemoryAmount()
        {
            double totalCapacity = 0;
            ObjectQuery objectQuery = new ObjectQuery("select * from Win32_PhysicalMemory");
            ManagementObjectSearcher searcher = new
            ManagementObjectSearcher(objectQuery);
            ManagementObjectCollection vals = searcher.Get();
            foreach (ManagementObject val in vals)
            {
                totalCapacity += Convert.ToDouble(val.GetPropertyValue("Capacity"));
            }

            return "ОЗУ " + (totalCapacity / 1048576) + "MegaBytes";
        }

        /// <summary>
        /// Получает информацию о жёстком диске на рабочей станции.
        /// </summary>
        /// <returns>Информация о жёстком диске на рабочей станции в строковом представлении.</returns>
        private static string GetHddInfo()
        {
            string info = string.Empty;
            // Get all the disk drives
            ManagementObjectSearcher mosDisks = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive");
            // Loop through each object (disk) retrieved by WMI
            foreach (ManagementObject val in mosDisks.Get())
            {
                // Add the HDD to the list (use the Model field as the item's caption)
                info = val["Model"].ToString(); //возможно нужно будет починить, чтобы флешки не ловило.
                info += " " + Math.Round(((((double)Convert.ToDouble(val["Size"]) / 1024) / 1024) / 1024), 2) + " GB)";
            }
            return info;
        }
    }

    /// <summary>
    /// Класс, отвечающий за генерирование отчётов сравнении о 
    /// проведённом процессе адаптации.
    /// </summary>
    public static class ReportComparingGenerator
    {
        /// <summary>
        /// Генерирует отчёт о выполненой адаптации.
        /// </summary>
        /// <param name="info">Объект, содержащий отчётные данные.</param>
        public static void GenerateReport(IList<ReportInformationContainer> infoList1,
                                          IList<ReportInformationContainer> infoList2)
        {
            XDocument document = ParseInfo(infoList1, infoList2);
            try
            {
                ReportInformationContainer info1 = infoList1[0];
                ReportInformationContainer info2 = infoList2[0];

                string curDateTime = DateTime.Now.ToString().Replace(':', '.').Replace('/', '.');
                using (StreamWriter outfile =
                    new StreamWriter(string.Format(@"{0}\{1}.html", MySettings.ReportsStoragePath,
                        info1.AlgorithmName + "_" + info2.AlgorithmName + "_"
                        + info1.MethodName + "_" + curDateTime)))
                {
                    using (XmlWriter writer = XmlWriter.Create(outfile))
                    {
                        // Load the style sheet.
                        XslCompiledTransform xslt = new XslCompiledTransform();
                        xslt.Load(MySettings.CurrentDirectoryPath + "\\XSL\\XSLTTemplateDivFiles.xsl");
                        // Execute the transform and output the results to a writer.
                        xslt.Transform(document.CreateReader(), writer);
                    }
                }
            }
            catch (NotSupportedException e)
            {
                MessageBox.Show("Ошибка при создании отчета" + e.Message, "Ошибка отчета", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            catch (Exception e)
            {
                MessageBox.Show("Ошибка при создании отчета" + e.Message, "Ошибка отчета", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// Создаёт XML документ в памяти для отображения отчётных данных.
        /// </summary>
        /// <param name="info">Объект, содержащий отчётные данные.</param>
        /// <returns>Сформированный XML документ.</returns>
        private static XDocument ParseInfo(IList<ReportInformationContainer> infoList1,
                                           IList<ReportInformationContainer> infoList2)
        {
            ReportInformationContainer info = infoList1[0];
            ReportInformationContainer info2 = infoList2[0];
            XDocument doc = new XDocument(
                    new XDeclaration("1.0", "utf-8", "yes"),
                    new XElement("Result",
                        new XElement("Date1", info.LaunchDate.ToString()),
                        
                        new XElement("AlgName1", info.AlgorithmName),
                        new XElement("MethodName1", info.MethodName),
                        new XElement("ExperimentCount1", info.ExperimentCount),
                        new XElement("Files"),
                        new XElement("Parameters1"),
                        new XElement("AllSize", ReporterFormula.GetAllSize(infoList1)),

                        new XElement("Date2", info2.LaunchDate.ToString()),
                        new XElement("AlgName2", info2.AlgorithmName),
                        new XElement("MethodName2", info2.MethodName),
                        new XElement("ExperimentCount2", info2.ExperimentCount),
/*                        new XElement("Files2"),*/
                        new XElement("Parameters2"))
                  );

            XElement settings = new XElement("Config",
                                                new XElement("Processor", GetProcessorID()),
                                                new XElement("Hdd", GetHddInfo()),
                                                new XElement("Memory", GetMemoryAmount()));

            for (int i = 0; i < infoList1.Count; ++i)
            {
                try
                {
                    XElement xElement = new XElement("File",
                    new XElement("Name", infoList1[i].FileNames[0].Name),
                    new XElement("Real_size1", infoList1[i].CalculateDirSize()),

                    new XElement("AlgName1", info.AlgorithmName),
                    new XElement("MethodName1", info.MethodName),
                    new XElement("ElapsedTime1", infoList1[i].TotalElapsedTimeFomatted),
                    new XElement("Recommended_size1", infoList1[i].recommendedResult),
                    new XElement("Best_size1", Math.Round(infoList1[i].bestSize)),
                    new XElement("Sup_time1", (infoList1[i].CalculateSupTime() * 100).ToString() + "%"),
                    new XElement("Reg_time1", (infoList1[i].CalculateRegTime() * 100).ToString() + "%"),
                    new XElement("Sup_compression1", (infoList1[i].CalculateSupCompression() * 100).ToString() + "%"),
                    new XElement("Reg_compression1", (infoList1[i].CalculateRegCompression() * 100).ToString() + "%"),

                    new XElement("AlgName2", info2.AlgorithmName),
                    new XElement("MethodName2", info2.MethodName),
                    new XElement("ElapsedTime2", infoList2[i].TotalElapsedTimeFomatted),
                    new XElement("Recommended_size2", infoList2[i].recommendedResult),
                    new XElement("Best_size2", Math.Round(infoList2[i].bestSize)),
                    new XElement("Sup_time2", (infoList2[i].CalculateSupTime() * 100).ToString() + "%"),
                    new XElement("Reg_time2", (infoList2[i].CalculateRegTime() * 100).ToString() + "%"),
                    new XElement("Sup_compression2", (infoList2[i].CalculateSupCompression() * 100).ToString() + "%"),
                    new XElement("Reg_compression2", (infoList2[i].CalculateRegCompression() * 100).ToString() + "%"));

                    doc.Descendants("Files").First().Add(xElement);
                }
                catch (FileNotFoundException exp)
                {

                }
            }

            foreach (KeyValuePair<int, LinkedParameter> parameter in info2.FindedParameters)
            {
                XElement xElement = new XElement("Parameter2",
                    new XElement("Name2", parameter.Value.parameter.ShortDescription),
                    new XElement("Default2", parameter.Value.parameter.DefaultValue),
                    new XElement("Value2", parameter.Value.parameter.CurrentValue));
                doc.Descendants("Parameters2").First().Add(xElement);
            }

            doc.Descendants("Result").First().Add(settings);
            return doc;
        }

        /// <summary>
        /// Получает информацию о процессоре рабочей станции.
        /// </summary>
        /// <returns>Информация о процессоре рабочей станции в строковом представлении.</returns>
        private static string GetProcessorID()
        {
            string sCpuInfo = String.Empty;

            //*** Declare Management Class
            ManagementClass clsMgtClass = new ManagementClass("Win32_Processor");
            ManagementObjectCollection colMgtObjCol = clsMgtClass.GetInstances();

            //*** Loop Over Objects
            foreach (ManagementObject objMgtObj in colMgtObjCol)
            {
                //*** Only return cpuInfo from first CPU
                if (sCpuInfo == String.Empty)
                {
                    sCpuInfo = objMgtObj.Properties["Manufacturer"].Value.ToString();
                    sCpuInfo += " " + objMgtObj.Properties["NumberOfCores"].Value + "Cores";
                    sCpuInfo += " " + objMgtObj.Properties["CurrentClockSpeed"].Value + "MHz";
                }
            }

            return sCpuInfo;
        }

        /// <summary>
        /// Получает информацию о количестве оперативной памяти на рабочей станции.
        /// </summary>
        /// <returns>Информация о количестве оперативной памяти на рабочей станции 
        /// в строковом представлении.</returns>
        private static string GetMemoryAmount()
        {
            double totalCapacity = 0;
            ObjectQuery objectQuery = new ObjectQuery("select * from Win32_PhysicalMemory");
            ManagementObjectSearcher searcher = new
            ManagementObjectSearcher(objectQuery);
            ManagementObjectCollection vals = searcher.Get();
            foreach (ManagementObject val in vals)
            {
                totalCapacity += Convert.ToDouble(val.GetPropertyValue("Capacity"));
            }

            return "ОЗУ " + (totalCapacity / 1048576) + "MegaBytes";
        }

        /// <summary>
        /// Получает информацию о жёстком диске на рабочей станции.
        /// </summary>
        /// <returns>Информация о жёстком диске на рабочей станции в строковом представлении.</returns>
        private static string GetHddInfo()
        {
            string info = string.Empty;
            // Get all the disk drives
            ManagementObjectSearcher mosDisks = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive");
            // Loop through each object (disk) retrieved by WMI
            foreach (ManagementObject val in mosDisks.Get())
            {
                // Add the HDD to the list (use the Model field as the item's caption)
                info = val["Model"].ToString(); //возможно нужно будет починить, чтобы флешки не ловило.
                info += " " + Math.Round(((((double)Convert.ToDouble(val["Size"]) / 1024) / 1024) / 1024), 2) + " GB)";
            }
            return info;
        }
    }

    public static class ReporterSize
    {
        public static void Report(IList<ReportInformationContainer> infoList)
        {
            ReportFirstAlgorithm(infoList);
        }

        public static void ReportFirstAlgorithm(IList<ReportInformationContainer> infoList)
        {
            ReportInformationContainer info1 = infoList[0];

            string curDateTime = DateTime.Now.ToString().Replace(':', '.').Replace('/', '.');
            using (StreamWriter outfile =
                new StreamWriter(string.Format(@"{0}\{1}_Size.txt", MySettings.ReportsStoragePath,
                                 info1.AlgorithmName + "_" + info1.MethodName + "_" + curDateTime),
                                 false,
                                 System.Text.Encoding.Unicode))
            {
                for (int i = 0; i < infoList.Count; ++i)
                {
                    outfile.WriteLine(i + 1 + " " + Math.Round(infoList[i].recommendedResult / infoList[i].bestFitness));
                }
            }

            using (StreamWriter outfile =
                new StreamWriter(string.Format(@"{0}\{1}_Time.txt", MySettings.ReportsStoragePath,
                                 info1.AlgorithmName + "_" + info1.MethodName + "_" + curDateTime),
                                 false,
                                 System.Text.Encoding.Unicode))
            {
                for (int i = 0; i < infoList.Count; ++i)
                {
                    outfile.WriteLine(i + 1 + " " + infoList[i].TotalElapsedTimeFomatted);
                }
            }
        }
    }
    public static class ReporterFormula
    {
        public static int GetAllSize(IList<ReportInformationContainer> infoList)
        {
            int sum = 0;
            foreach (ReportInformationContainer reportInfo in infoList)
            {
                sum += (int)reportInfo.CalculateDirSize();
            }

            return sum;
        }
    }
}
